
#include "GameBitMap.h"



//
int CBitMapFile::LoadBitMapFile(char *filename, int dd_pixel_format)
{
	// this function opens a bitmap file and loads the data into bitmap

	int file_handle,  // the file handle
		index;        // looping index

	UCHAR   *temp_buffer = NULL; // used to convert 24 bit images to 16 bit
	OFSTRUCT file_data;          // the file data information

	// open the file if it exists
	if ((file_handle = OpenFile(filename,&file_data,OF_READ))==-1)
		return -1;

	// now load the bitmap file header
	_lread(file_handle, &m_bitmap.bitmapfileheader,sizeof(BITMAPFILEHEADER));

	// test if this is a bitmap file
	if (m_bitmap.bitmapfileheader.bfType!=BITMAP_ID)
	{
		// close the file
		_lclose(file_handle);

		// return error
		return -1;
	} // end if

	UnloadBitmapFile();

	// now we know this is a bitmap, so read in all the sections

	// first the bitmap infoheader

	// now load the bitmap file header
	_lread(file_handle, &m_bitmap.bitmapinfoheader,sizeof(BITMAPINFOHEADER));

	// now load the color palette if there is one
	if (m_bitmap.bitmapinfoheader.biBitCount == 8)
	{
		_lread(file_handle, &m_bitmap.palette,MAX_COLORS_PALETTE*sizeof(PALETTEENTRY));

		// now set all the flags in the palette correctly and fix the reversed 
		// BGR RGBQUAD data format
		for (index=0; index < MAX_COLORS_PALETTE; index++)
		{
			// reverse the red and green fields
			int temp_color                = m_bitmap.palette[index].peRed;
			m_bitmap.palette[index].peRed  = m_bitmap.palette[index].peBlue;
			m_bitmap.palette[index].peBlue = temp_color;

			// always set the flags word to this
			m_bitmap.palette[index].peFlags = PC_NOCOLLAPSE;
		} // end for index

	} // end if

	// finally the image data itself
	_llseek(file_handle,-(int)(m_bitmap.bitmapinfoheader.biSizeImage),SEEK_END);

	// now read in the image
	if (m_bitmap.bitmapinfoheader.biBitCount==8 || m_bitmap.bitmapinfoheader.biBitCount==16) 
	{
		// delete the last image if there was one
		if (m_bitmap.buffer)
			free(m_bitmap.buffer);

		// allocate the memory for the image
		if (!(m_bitmap.buffer = (UCHAR *)malloc(m_bitmap.bitmapinfoheader.biSizeImage)))
		{
			// close the file
			_lclose(file_handle);

			// return error
			return -1;
		} // end if

		// now read it in
		_lread(file_handle,m_bitmap.buffer,m_bitmap.bitmapinfoheader.biSizeImage);

	} // end if
	else
		if (m_bitmap.bitmapinfoheader.biBitCount==24)
		{
			// allocate temporary buffer to load 24 bit image
			if (!(temp_buffer = (UCHAR *)malloc(m_bitmap.bitmapinfoheader.biSizeImage)))
			{
				// close the file
				_lclose(file_handle);

				// return error
				return -1;
			} // end if

			// allocate final 16 bit storage buffer
			if (!(m_bitmap.buffer=(UCHAR *)malloc(2*m_bitmap.bitmapinfoheader.biWidth*m_bitmap.bitmapinfoheader.biHeight)))
			{
				// close the file
				_lclose(file_handle);

				// release working buffer
				free(temp_buffer);

				// return error
				return -1;
			} // end if

			// now read the file in
			_lread(file_handle,temp_buffer,m_bitmap.bitmapinfoheader.biSizeImage);

			// now convert each 24 bit RGB value into a 16 bit value
			for (index=0; index < m_bitmap.bitmapinfoheader.biWidth*m_bitmap.bitmapinfoheader.biHeight; index++)
			{
				// build up 16 bit color word
				USHORT color;

				// build pixel based on format of directdraw surface
				if (dd_pixel_format==DD_PIXEL_FORMAT555)
				{
					// extract RGB components (in BGR order), note the scaling
					UCHAR blue  = (temp_buffer[index*3 + 0] >> 3),
						green = (temp_buffer[index*3 + 1] >> 3),
						red   = (temp_buffer[index*3 + 2] >> 3); 
					// use the 555 macro
					color = _RGB16BIT555(red,green,blue);
				} // end if 555
				else
					if (dd_pixel_format==DD_PIXEL_FORMAT565) 
					{
						// extract RGB components (in BGR order), note the scaling
						UCHAR blue  = (temp_buffer[index*3 + 0] >> 3),
							green = (temp_buffer[index*3 + 1] >> 2),
							red   = (temp_buffer[index*3 + 2] >> 3);

						// use the 565 macro
						color = _RGB16BIT565(red,green,blue);

					} // end if 565

					// write color to buffer
					((USHORT *)m_bitmap.buffer)[index] = color;

			} // end for index

			// finally write out the correct number of bits
			m_bitmap.bitmapinfoheader.biBitCount=16;

			// release working buffer
			free(temp_buffer);

		} // end if 24 bit
		else
		{
			// serious problem
			return -1;

		} // end else

#if 0
		// write the file info out 
		printf("\nfilename:%s \nsize=%d \nwidth=%d \nheight=%d \nbitsperpixel=%d \ncolors=%d \nimpcolors=%d",
			filename,
			m_bitmap.bitmapinfoheader.biSizeImage,
			m_bitmap.bitmapinfoheader.biWidth,
			m_bitmap.bitmapinfoheader.biHeight,
			m_bitmap.bitmapinfoheader.biBitCount,
			m_bitmap.bitmapinfoheader.biClrUsed,
			m_bitmap.bitmapinfoheader.biClrImportant);
#endif

		// close the file
		_lclose(file_handle);

		// flip the bitmap
		Flip_Bitmap(m_bitmap.buffer, 
			m_bitmap.bitmapinfoheader.biWidth*(m_bitmap.bitmapinfoheader.biBitCount/8), 
			m_bitmap.bitmapinfoheader.biHeight);

		// return success
		return 0;

} // end Load_Bitmap_File

///////////////////////////////////////////////////////////

int CBitMapFile::UnloadBitmapFile()
{
	// this function releases all memory associated with "bitmap"
	if (m_bitmap.buffer)
	{
		// release memory
		free(m_bitmap.buffer);

		// reset pointer
		m_bitmap.buffer = NULL;

	} // end if

	// return success
	return 0;

} // end Unload_Bitmap_File

///////////////////////////////////////////////////////////
//



int CBitMapImage::CreateImage(int x, int y, int width, int height, int bpp)
{
	// this function is used to intialize a bitmap, 8 or 16 bit

	// allocate the memory
	if (!(m_image.buffer = (UCHAR *)malloc(width*height*(bpp>>3))))
		return -1;

	// initialize variables
	m_image.state     = BITMAP_STATE_ALIVE;
	m_image.attr      = 0;
	m_image.width     = width;
	m_image.height    = height;
	m_image.bpp       = bpp;
	m_image.x         = x;
	m_image.y         = y;
	m_image.num_bytes = width*height*(bpp>>3);

	// clear memory out
	memset(m_image.buffer,0,width*height*(bpp>>3));

	// return success
	return 0;

} // end CreateImage

///////////////////////////////////////////////////////////////////////////////

int CBitMapImage::DestroyImage()
{
	// this function releases the memory associated with a bitmap

	if (m_image.buffer)
	{
		// free memory and reset vars
		free(m_image.buffer);

		// set all vars in structure to 0
		memset(&m_image,0,sizeof(BITMAP_IMAGE));

		m_image.buffer = NULL;

		// return success
		return 0;

	} // end if
	else  // invalid entry pointer of the object wasn't initialized
		return -1;

} // end DestroyImage

///////////////////////////////////////////////////////////

int CBitMapImage::Draw_BitMap(uint8 *dest_buffer, int lpitch, int transparent)
{
	// this function draws the bitmap onto the destination memory surface
	// if transparent is 1 then color 0 (8bit) or 0.0.0 (16bit) will be transparent
	// note this function does NOT clip, so be carefull!!!

	// test if this bitmap is loaded
	if (!(m_image.attr & BITMAP_ATTR_LOADED))
		return -1;

	uint8 *dest_addr,   // starting address of bitmap in destination
		*source_addr; // starting adddress of bitmap data in source

	uint8 pixel;        // used to hold pixel value

	int index,          // looping vars
		pixel_x;

	// compute starting destination address
	dest_addr = dest_buffer + m_image.y*lpitch + m_image.x;

	// compute the starting source address
	source_addr = m_image.buffer;

	// is this bitmap transparent
	if (transparent)
	{
		// copy each line of bitmap into destination with transparency
		for (index=0; index<m_image.height; index++)
		{
			// copy the memory
			for (pixel_x=0; pixel_x<m_image.width; pixel_x++)
			{
				if ((pixel = source_addr[pixel_x])!=0)
					dest_addr[pixel_x] = pixel;

			} // end if

			// advance all the pointers
			dest_addr   += lpitch;
			source_addr += m_image.width;

		} // end for index
	} // end if
	else
	{
		// non-transparent version
		// copy each line of bitmap into destination

		for (index=0; index < m_image.height; index++)
		{
			// copy the memory
			memcpy(dest_addr, source_addr, m_image.width);

			// advance all the pointers
			dest_addr   += lpitch;
			source_addr += m_image.width;

		} // end for index

	} // end else

	// return success
	return 0;

} // end Draw_Bitmap

///////////////////////////////////////////////////////////////

int CBitMapImage::Draw_BitMap16(uint8 *dest_buffer, int lpitch, int transparent)
{
	// this function draws the bitmap onto the destination memory surface
	// if transparent is 1 then color 0 (8bit) or 0.0.0 (16bit) will be transparent
	// note this function does NOT clip, so be carefull!!!

	// test if this bitmap is loaded
	if (!(m_image.attr & BITMAP_ATTR_LOADED))
		return -1;

	uint16 *dest_addr,   // starting address of bitmap in destination
		*source_addr; // starting adddress of bitmap data in source

	uint16 pixel;        // used to hold pixel value

	int index,           // looping vars
		pixel_x,
		lpitch_2 = lpitch >> 1; // lpitch in USHORT terms

	// compute starting destination address
	dest_addr = ((uint16 *)dest_buffer) + m_image.y*lpitch_2 + m_image.x;

	// compute the starting source address
	source_addr = (uint16 *)m_image.buffer;

	// is this bitmap transparent
	if (transparent)
	{
		// copy each line of bitmap into destination with transparency
		for (index=0; index<m_image.height; index++)
		{
			// copy the memory
			for (pixel_x=0; pixel_x<m_image.width; pixel_x++)
			{
				if ((pixel = source_addr[pixel_x])!=0)
					dest_addr[pixel_x] = pixel;

			} // end if

			// advance all the pointers
			dest_addr   += lpitch_2;
			source_addr += m_image.width;

		} // end for index
	} // end if
	else
	{
		// non-transparent version
		// copy each line of bitmap into destination

		int source_bytes_per_line = m_image.width*2;

		for (index=0; index < m_image.height; index++)
		{
			// copy the memory
			memcpy(dest_addr, source_addr, source_bytes_per_line);

			// advance all the pointers
			dest_addr   += lpitch_2;
			source_addr += m_image.width;

		} // end for index

	} // end else

	// return success
	return 0;

} // end Draw_Bitmap16

///////////////////////////////////////////////////////////////////////////////

int CBitMapImage::Load_Image_Bitmap(CBitMapFile* pBitMapFile,
								   int cx,int cy,   // cell or absolute pos. to scan image from
								   int mode)        // if 0 then cx,cy is cell position, else 
								   // cx,cy are absolute coords
{
	// this function extracts a bitmap out of a bitmap file

	if (pBitMapFile == NULL)
		return -1;

	BITMAP_FILE_PTR pBitMap = pBitMapFile->GetBitMap();
	if (pBitMap == NULL)
		return -1;


	uint8 *source_ptr,   // working pointers
		*dest_ptr;

	// test the mode of extraction, cell based or absolute
	if (mode == BITMAP_EXTRACT_MODE_CELL)
	{
		// re-compute x,y
		cx = cx*(m_image.width+1) + 1;
		cy = cy*(m_image.height+1) + 1;
	} // end if

	// extract bitmap data
	source_ptr = pBitMap->buffer +
		cy * pBitMap->bitmapinfoheader.biWidth + cx;

	// assign a pointer to the bimap image
	dest_ptr = (uint8 *)m_image.buffer;

	// iterate thru each scanline and copy bitmap
	for (int index_y=0; index_y<m_image.height; index_y++)
	{
		// copy next line of data to destination
		memcpy(dest_ptr, source_ptr,m_image.width);

		// advance pointers
		dest_ptr   += m_image.width;
		source_ptr += pBitMap->bitmapinfoheader.biWidth;
	} // end for index_y

	// set state to loaded
	m_image.attr |= BITMAP_ATTR_LOADED;

	// return success
	return 0;

} // end Load_Image_Bitmap

///////////////////////////////////////////////////////////

int CBitMapImage::Load_Image_Bitmap16(CBitMapFile* pBitMapFile,
									 int cx,int cy,   // cell or absolute pos. to scan image from
									 int mode)        // if 0 then cx,cy is cell position, else 
									 // cx,cy are absolute coords
{
	// this function extracts a 16-bit bitmap out of a 16-bit bitmap file

	if (pBitMapFile == NULL)
		return -1;

	BITMAP_FILE_PTR pBitMap = pBitMapFile->GetBitMap();
	if (pBitMap == NULL)
		return -1;

	// must be a 16bit bitmap
	USHORT *source_ptr,   // working pointers
		*dest_ptr;

	// test the mode of extraction, cell based or absolute
	if (mode==BITMAP_EXTRACT_MODE_CELL)
	{
		// re-compute x,y
		cx = cx*(m_image.width+1) + 1;
		cy = cy*(m_image.height+1) + 1;
	} // end if

	// extract bitmap data
	source_ptr = (USHORT *)pBitMap->buffer + 
		cy*pBitMap->bitmapinfoheader.biWidth+cx;

	// assign a pointer to the bimap image
	dest_ptr = (USHORT *)m_image.buffer;

	int bytes_per_line = m_image.width*2;

	// iterate thru each scanline and copy bitmap
	for (int index_y=0; index_y < m_image.height; index_y++)
	{
		// copy next line of data to destination
		memcpy(dest_ptr, source_ptr,bytes_per_line);

		// advance pointers
		dest_ptr   += m_image.width;
		source_ptr += pBitMap->bitmapinfoheader.biWidth;
	} // end for index_y

	// set state to loaded
	m_image.attr |= BITMAP_ATTR_LOADED;

	// return success
	return 0;

} // end Load_Image_Bitmap16

///////////////////////////////////////////////////////////

int Scroll_Bitmap(BITMAP_IMAGE_PTR image, int dx, int dy)
{
	// this function scrolls a bitmap

	CBitMapImage	temp_image;


	// are the parms valid 
	if (!image || (dx==0 && dy==0))
		return(0);


	// scroll on x-axis first
	if (dx!=0)
	{
		// step 1: normalize scrolling amount
		dx %= image->width;

		// step 2: which way?
		if (dx > 0)
		{
			// scroll right
			// create bitmap to hold region that is scrolled around
			temp_image.CreateImage(0, 0, dx, image->height, image->bpp);

			// copy region we are going to scroll and wrap around
			Copy_Bitmap(temp_image.GetImage(),0,0, 
				image, image->width-dx,0, 
				dx, image->height);

			// set some pointers up
			UCHAR *source_ptr = image->buffer;  // start of each line
			int shift         = (image->bpp >> 3)*dx;

			// now scroll image to right "scroll" pixels
			for (int y=0; y < image->height; y++)
			{
				// scroll the line over
				memmove(source_ptr+shift, source_ptr, (image->width-dx)*(image->bpp >> 3));

				// advance to the next line
				source_ptr+=((image->bpp >> 3)*image->width);
			} // end for

			// and now copy it back
			Copy_Bitmap(image, 0,0, temp_image.GetImage(),0,0, 
				dx, image->height);           

		} // end if
		else
		{
			// scroll left
			dx = -dx; // invert sign

			// create bitmap to hold region that is scrolled around
			temp_image.CreateImage(0, 0, dx, image->height, image->bpp);

			// copy region we are going to scroll and wrap around
			Copy_Bitmap(temp_image.GetImage(),0,0, 
				image, 0,0, 
				dx, image->height);

			// set some pointers up
			UCHAR *source_ptr = image->buffer;  // start of each line
			int shift         = (image->bpp >> 3)*dx;

			// now scroll image to left "scroll" pixels
			for (int y=0; y < image->height; y++)
			{
				// scroll the line over
				memmove(source_ptr, source_ptr+shift, (image->width-dx)*(image->bpp >> 3));

				// advance to the next line
				source_ptr+=((image->bpp >> 3)*image->width);
			} // end for

			// and now copy it back
			Copy_Bitmap(image, image->width-dx,0, temp_image.GetImage(),0,0, 
				dx, image->height);           

		} // end else
	} // end scroll on x-axis


	// return success
	return 0;

} // end Scroll_Bitmap

///////////////////////////////////////////////////////////

int Copy_Bitmap(BITMAP_IMAGE_PTR dest_bitmap, int dest_x, int dest_y, 
				BITMAP_IMAGE_PTR source_bitmap, int source_x, int source_y, 
				int width, int height)
{
	// this function copies a bitmap from one source to another

	// make sure the pointers are at least valid
	if (!dest_bitmap || !source_bitmap)
		return -1;

	// do some computations
	int bytes_per_pixel = (source_bitmap->bpp >> 3);

	// create some pointers
	UCHAR *source_ptr = source_bitmap->buffer + (source_x + source_y*source_bitmap->width)*bytes_per_pixel;
	UCHAR *dest_ptr   = dest_bitmap->buffer   + (dest_x   + dest_y  *dest_bitmap->width)  *bytes_per_pixel;

	// now copying is easy :)
	for (int y = 0; y < height; y++)
	{
		// copy this line
		memcpy(dest_ptr, source_ptr, bytes_per_pixel*width);

		// advance the pointers
		source_ptr+=(source_bitmap->width*bytes_per_pixel);
		dest_ptr  +=(dest_bitmap->width*bytes_per_pixel);
	} // end for

	// return success
	return 0;

} // end Copy_Bitmap


int Flip_Bitmap(UCHAR *image, int bytes_per_line, int height)
{
	// this function is used to flip bottom-up .BMP images

	UCHAR *buffer; // used to perform the image processing
	int index;     // looping index

	// allocate the temporary buffer
	if (!(buffer = (UCHAR *)malloc(bytes_per_line*height)))
		return -1;

	// copy image to work area
	memcpy(buffer,image,bytes_per_line*height);

	// flip vertically
	for (index=0; index < height; index++)
		memcpy(&image[((height-1) - index)*bytes_per_line],
		&buffer[index*bytes_per_line], bytes_per_line);

	// release the memory
	free(buffer);

	// return success
	return 0;

} // end Flip_Bitmap